﻿using System;
using System.Runtime.CompilerServices;

namespace IOP.Extension.Convert
{
    /// <summary>
    /// 数值转换扩展
    /// </summary>
    public static class NumberConvertExtension
    {
        /// <summary>
        /// 将16位有符号整数转换为字节数组
        /// </summary>
        /// <param name="number"></param>
        /// <param name="endian">字节序</param>
        /// <returns></returns>
        [Obsolete("Please use WriteBytes for replace")]
        public static byte[] ToBytes(this short number,Endian endian = Endian.BigEndian)
        {
            var bytes = new byte[2];
            switch (endian)
            {
                case Endian.LittleEndian:
                    for (int i = 0; i < 2; i++)
                    {
                        bytes[i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
                case Endian.BigEndian:
                    for (int i = 1; i >= 0; i--)
                    {
                        bytes[1 - i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
            }
            return bytes;
        }
        /// <summary>
        /// 将数据写入字节序列并移动下标
        /// </summary>
        /// <param name="number"></param>
        /// <param name="target"></param>
        /// <param name="index"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void WriteBytes(this short number, ref Span<byte> target, ref int index, Endian endian = Endian.BigEndian)
        {
            int shortLength = sizeof(short);
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < shortLength; i++)
                        target[index + i] = (byte)(number >> (8 * (shortLength - 1 - i)) & 0xFF);
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < shortLength; i++)
                        target[index + i] = (byte)(number >> (8 * i) & 0xFF);
                    break;
            }
            index += shortLength;
        }
        /// <summary>
        /// 从字节序列获取16bit有符号数字并移动下标
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <param name="result"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void GetValueFromBytes(this ReadOnlySpan<byte> source, ref int index, out short result, Endian endian = Endian.BigEndian)
        {
            int shortLength = sizeof(short);
            result = 0;
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < shortLength; i++)
                        result |= (short)(source[index + i] << (8 * (shortLength - 1 - i) & 0xFF));
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < shortLength; i++)
                        result |= (short)(source[index + i] << ((8 * i) & 0xFF));
                    break;
            }
            index += shortLength;
        }

        /// <summary>
        /// 将16位无符号整数转换为字节数组
        /// </summary>
        /// <param name="number"></param>
        /// <param name="endian">字节序S</param>
        /// <returns></returns>
        [Obsolete("Please use WriteBytes for replace")]
        public static byte[] ToBytes(this ushort number, Endian endian = Endian.BigEndian)
        {
            var bytes = new byte[2];
            switch (endian)
            {
                case Endian.LittleEndian:
                    for (int i = 0; i < 2; i++)
                    {
                        bytes[i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
                case Endian.BigEndian:
                    for (int i = 1; i >= 0; i--)
                    {
                        bytes[1 - i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
            }
            return bytes;
        }
        /// <summary>
        /// 将数据写入字节序列并移动下标
        /// </summary>
        /// <param name="number"></param>
        /// <param name="target"></param>
        /// <param name="index"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void WriteBytes(this ushort number, ref Span<byte> target, ref int index, Endian endian = Endian.BigEndian)
        {
            int ushortLength = sizeof(ushort);
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < ushortLength; i++)
                        target[index + i] = (byte)(number >> (8 * (ushortLength - 1 - i)) & 0xFF);
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < ushortLength; i++)
                        target[index + i] = (byte)(number >> (8 * i) & 0xFF);
                    break;
            }
            index += ushortLength;
        }
        /// <summary>
        /// 从字节序列获取16bit无符号数字并移动下标
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <param name="result"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void GetValueFromBytes(this ReadOnlySpan<byte> source, ref int index, out ushort result, Endian endian = Endian.BigEndian)
        {
            int ushortLength = sizeof(ushort);
            result = 0;
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < ushortLength; i++)
                        result |= (ushort)(source[index + i] << (8 * (ushortLength - 1 - i) & 0xFF));
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < ushortLength; i++)
                        result |= (ushort)(source[index + i] << ((8 * i) & 0xFF));
                    break;
            }
            index += ushortLength;
        }

        /// <summary>
        /// 将32位有符号整数转换为字节数组
        /// </summary>
        /// <param name="number"></param>
        /// <param name="endian"></param>
        /// <returns></returns>
        [Obsolete("Please use WriteBytes for replace")]
        public static byte[] ToBytes(this int number, Endian endian = Endian.BigEndian)
        {
            var bytes = new byte[4];
            switch (endian)
            {
                case Endian.LittleEndian:
                    for (int i = 0; i < 4; i++)
                    {
                        bytes[i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
                case Endian.BigEndian:
                    for (int i = 3; i >= 0; i--)
                    {
                        bytes[3 - i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
            }
            return bytes;
        }
        /// <summary>
        /// 将数据写入字节序列并移动下标
        /// </summary>
        /// <param name="number"></param>
        /// <param name="target"></param>
        /// <param name="index"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void WriteBytes(this int number, ref Span<byte> target, ref int index, Endian endian = Endian.BigEndian)
        {
            int intLength = sizeof(int);
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < intLength; i++)
                        target[index + i] = (byte)(number >> (8 * (intLength - 1 - i)) & 0xFF);
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < intLength; i++)
                        target[index + i] = (byte)(number >> (8 * i) & 0xFF);
                    break;
            }
            index += intLength;
        }
        /// <summary>
        /// 从字节序列获取32bit有符号数字并移动下标
        /// </summary>
        /// <param name="source"></param>
        /// <param name="result"></param>
        /// <param name="index"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void GetValueFromBytes(this ReadOnlySpan<byte> source, ref int index, out int result, Endian endian = Endian.BigEndian)
        {
            int intLength = sizeof(int);
            result = 0;
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < intLength; i++)
                        result |= source[index + i] << (8 * (intLength - 1 - i) & 0xFF);
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < intLength; i++)
                        result |= source[index + i] << ((8 * i) & 0xFF);
                    break;
            }
            index += intLength;
        }

        /// <summary>
        /// 将32位无符号整数转换为字节数组
        /// </summary>
        /// <param name="number"></param>
        /// <param name="endian"></param>
        /// <returns></returns>
        [Obsolete("Please use WriteBytes for replace")]
        public static byte[] ToBytes(this uint number, Endian endian = Endian.BigEndian)
        {
            var bytes = new byte[4];
            switch (endian)
            {
                case Endian.LittleEndian:
                    for (int i = 0; i < 4; i++)
                    {
                        bytes[i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
                case Endian.BigEndian:
                    for (int i = 3; i >= 0; i--)
                    {
                        bytes[3 - i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
            }
            return bytes;
        }
        /// <summary>
        /// 将数据写入字节序列并移动下标
        /// </summary>
        /// <param name="number"></param>
        /// <param name="target"></param>
        /// <param name="index"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void WriteBytes(this uint number, ref Span<byte> target, ref int index, Endian endian = Endian.BigEndian)
        {
            int uintLength = sizeof(uint);
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < uintLength; i++)
                        target[index + i] = (byte)(number >> (8 * (uintLength - 1 - i)) & 0xFF);
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < uintLength; i++)
                        target[index + i] = (byte)(number >> (8 * i) & 0xFF);
                    break;
            }
            index += uintLength;
        }
        /// <summary>
        /// 从字节序列获取32bit无符号数字并移动下标
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <param name="result"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void GetValueFromBytes(this ReadOnlySpan<byte> source, ref int index, out uint result, Endian endian = Endian.BigEndian)
        {
            int uintLength = sizeof(uint);
            result = 0;
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < uintLength; i++)
                        result |= (uint)(source[index + i] << (8 * (uintLength - 1 - i) & 0xFF));
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < uintLength; i++)
                        result |= (uint)(source[index + i] << ((8 * i) & 0xFF));
                    break;
            }
            index += uintLength;
        }

        /// <summary>
        /// 将64位有符号整数转换为字节数组
        /// </summary>
        /// <param name="number"></param>
        /// <param name="endian"></param>
        /// <returns></returns>
        [Obsolete("Please use WriteBytes for replace")]
        public static byte[] ToBytes(this long number, Endian endian = Endian.BigEndian)
        {
            var bytes = new byte[8];
            switch (endian)
            {
                case Endian.LittleEndian:
                    for (int i = 0; i < 8; i++)
                    {
                        bytes[i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
                case Endian.BigEndian:
                    for (int i = 7; i >= 0; i--)
                    {
                        bytes[7 - i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
            }
            return bytes;
        }
        /// <summary>
        /// 将数据写入字节序列并移动下标
        /// </summary>
        /// <param name="number"></param>
        /// <param name="target"></param>
        /// <param name="index"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void WriteBytes(this long number, ref Span<byte> target, ref int index, Endian endian = Endian.BigEndian)
        {
            int longLength = sizeof(long);
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < longLength; i++)
                        target[index + i] = (byte)(number >> ((8 * (longLength - 1 - i)) & 0xFF));
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < longLength; i++)
                        target[index + i] = (byte)(number >> (8 * i) & 0xFF);
                    break;
            }
            index += longLength;
        }
        /// <summary>
        /// 从字节序列获取64bit有符号数字并移动下标
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <param name="result"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void GetValueFromBytes(this ReadOnlySpan<byte> source, ref int index, out long result, Endian endian = Endian.BigEndian)
        {
            int longLength = sizeof(long);
            result = 0;
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < longLength; i++)
                        result |= (long)source[index + i] << ((8 * (longLength - 1 - i)) & 0xFF);
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < longLength; i++)
                        result |= (long)source[index + i] << ((8 * i) & 0xFF);
                    break;
            }
            index += longLength;
        }


        /// <summary>
        /// 将64位无符号整数转换为字节数组
        /// </summary>
        /// <param name="number"></param>
        /// <param name="endian"></param>
        /// <returns></returns>
        [Obsolete("Please use WriteBytes for replace")]
        public static byte[] ToBytes(this ulong number, Endian endian = Endian.BigEndian)
        {
            var bytes = new byte[8];
            switch (endian)
            {
                case Endian.LittleEndian:
                    for (int i = 0; i < 8; i++)
                    {
                        bytes[i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
                case Endian.BigEndian:
                    for (int i = 7; i >= 0; i--)
                    {
                        bytes[7 - i] = (byte)(number >> (8 * i) & 0xFF);
                    }
                    break;
            }
            return bytes;
        }
        /// <summary>
        /// 将数据写入字节序列并移动下标
        /// </summary>
        /// <param name="number"></param>
        /// <param name="target"></param>
        /// <param name="index"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void WriteBytes(this ulong number, ref Span<byte> target, ref int index, Endian endian = Endian.BigEndian)
        {
            int ulongLength = sizeof(ulong);
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < ulongLength; i++)
                        target[index + i] = (byte)(number >> ((8 * (ulongLength - 1 - i)) & 0xFF));
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < ulongLength; i++)
                        target[index + i] = (byte)(number >> (8 * i) & 0xFF);
                    break;
            }
            index += ulongLength;
        }
        /// <summary>
        /// 从字节序列获取64bit无符号数字并移动下标
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <param name="result"></param>
        /// <param name="endian"></param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void GetValueFromBytes(this ReadOnlySpan<byte> source, ref int index, out ulong result, Endian endian = Endian.BigEndian)
        {
            int ulongLength = sizeof(ulong);
            result = 0;
            switch (endian)
            {
                case Endian.BigEndian:
                    for (int i = 0; i < ulongLength; i++)
                        result |= (ulong)source[index + i] << ((8 * (ulongLength - 1 - i)) & 0xFF);
                    break;
                case Endian.LittleEndian:
                    for (int i = 0; i < ulongLength; i++)
                        result |= (ulong)source[index + i] << ((8 * i) & 0xFF);
                    break;
            }
            index += ulongLength;
        }
    }
}
